using System;
using System.Collections.Generic;
using System.Text;
using System.IO;
using System.Threading;
using Microsoft.Xna.Framework.Graphics;
using Microsoft.Xna.Framework.Graphics.PackedVector;
using Microsoft.Xna.Framework.Content;
using Microsoft.Xna.Framework;

namespace Scurvy.Media.VideoModel
{
    internal enum Affinity
    {
        Two = 1,
        Four = 3,
        Five = 4,
        Six = 5
    }

    internal sealed class VideoStreamer : AsyncWorker
    {
        public byte[] Data;
        //public Bgr565[] Pixels;
        private Stream _stream;
        private BinaryReader reader;
        public long StreamBeginIndex;


        public VideoStreamer(Stream stream)
        {
            _stream = stream;
            StreamBeginIndex = stream.Position;
            reader = new BinaryReader(stream);
        }

        protected override void DoWork()
        {
            int frameSize = reader.ReadInt32();
            if (Data == null)
            {
                Data = new byte[frameSize];
            }
            _stream.Read(Data, 0, frameSize);
        }

        public static void ReadWholeArray(Stream stream, byte[] data)
        {
            int offset = 0;
            int remaining = data.Length;
            while (remaining > 0)
            {
                int read = stream.Read(data, offset, remaining);
                if (read <= 0)
                    throw new EndOfStreamException
                        (String.Format("End of stream reached with {0} bytes left to read", remaining));
                remaining -= read;
                offset += read;
            }
        }

        internal void Reset()
        {
            this._stream.Seek(StreamBeginIndex, SeekOrigin.Begin);
        }
    }

    internal abstract class AsyncWorker
    {
        public AutoResetEvent Event = new AutoResetEvent(true);
        public bool Stop = false;

        protected abstract void DoWork();

        public void Work()
        {
#if XBOX360
            //_thread.SetProcessorAffinity((int)affinity);
#endif
            while (!Stop)
            {
                DoWork();
                Event.WaitOne();
            }
        }

        public void Update()
        {
            Event.Set();
        }
    }

    internal sealed class AsyncInvoker
    {
        private Thread _thread;
        private AsyncWorker _worker;

        public AsyncInvoker(AsyncWorker del)
            : this(del, Affinity.Two)
        {
        }

        public AsyncInvoker(AsyncWorker del, Affinity affinity)
        {
            _worker = del;
            _thread = new Thread(del.Work);
            _thread.IsBackground = true;


            _thread.Start();
        }

    }

    /// <summary>
    /// Streams the video from the file stream that the content manager opens
    /// </summary>
    public class VideoStream : Video
    {
        #region Private Fields

        private Texture2D _texture;
        private Texture2D _texture1;
        private Texture2D _texture2;
        private int numberOfFrames;
        private double frameRate;
        private int _lastFrame = 0;
        private bool _usecompression;

        private AsyncInvoker _invoker;
        private VideoStreamer _streamer;

        #endregion

        internal VideoStream(ContentReader creader, int numberOfFrames, double frameRate, int height, int width, GraphicsDevice device, bool usecompression)
            : base(numberOfFrames, frameRate)
        {
            Stream stream = creader.BaseStream;

            GraphicsDeviceManager gdm = (GraphicsDeviceManager)creader.ContentManager.ServiceProvider.GetService(typeof(IGraphicsDeviceManager));

            _usecompression = usecompression;
            SurfaceFormat format = usecompression ? SurfaceFormat.Dxt1 : SurfaceFormat.Bgr565;
            format = SelectSurfaceFormat(usecompression);

            _texture1 = new Texture2D(gdm.GraphicsDevice, width, height, 1, TextureUsage.None, format);
            _texture2 = new Texture2D(gdm.GraphicsDevice, width, height, 1, TextureUsage.None, format);
            _texture = _texture1;

            this.numberOfFrames = numberOfFrames;
            this.frameRate = frameRate;

            _streamer = new VideoStreamer(stream);
            _invoker = new AsyncInvoker(_streamer);


        }

        private static SurfaceFormat SelectSurfaceFormat(bool usecompression)
        {
            SurfaceFormat format;
            if (usecompression)
            {
                format = SurfaceFormat.Dxt1;
            }
            else if (Platform.IsXBox)
            {
                format = SurfaceFormat.Bgr565;
            }
            else
            {
                format = SurfaceFormat.Bgr565;
            }
            return format;
        }

        /// <exception cref="InvalidContentManagerException">This exception will be thrown
        /// if the incorrect type of content manager was used to instantiate this video.</exception>
        public override Microsoft.Xna.Framework.Graphics.Texture2D CurrentTexture
        {
            get
            {
                try
                {
                    if (_lastFrame != this.CurrentIndex)
                    {
                        if (_lastFrame > this.CurrentIndex)
                        {
                            _streamer.Reset();

                        }

                        //wait for loader to finish

                        //load the texture
                        _texture = (_texture == _texture1) ? _texture2 : _texture1;

                        _texture.SetData<Byte>(_streamer.Data);




                        _lastFrame = this.CurrentIndex;

                        _streamer.Update();
                    }
                    return _texture;
                }
                catch (ObjectDisposedException ode)
                {
                    throw new InvalidContentManagerException(ode);
                }
            }
        }




    }
}
